// "use strict"
// const delta = 0x9E3779B9;
// import buffer from "buffer";


// export class xxtea{
//     private static longArrayToString(data, includeLength) {
//         let length = data.length
//             , n = (length - 1) << 2;
//         if (includeLength) {
//             let m = data[length - 1];
//             if ((m < n - 3) || (m > n)) return null;
//             n = m;
//         }
//         for (let i = 0; i < length; ++i) {
//             data[i] = String.fromCharCode(
//                 data[i] & 0xff,
//                 data[i] >>> 8 & 0xff,
//                 data[i] >>> 16 & 0xff,
//                 data[i] >>> 24 & 0xff
//             );
//         }
//         if (includeLength) {
//             return data.join("").substring(0, n);
//         }
//         else {
//             return data.join("");
//         }
//     }
    
//     private static stringToLongArray(string, includeLength) {
//         let length = string.length;
//         let result = [];
//         for (let i = 0; i < length; i += 4) {
//             result[i >> 2] = string.charCodeAt(i) |
//                 string.charCodeAt(i + 1) << 8 |
//                 string.charCodeAt(i + 2) << 16 |
//                 string.charCodeAt(i + 3) << 24;
//         }
//         if (includeLength) {
//             result[result.length] = length;
//         }
//         return result;
//     }
//     public static encrypt(string, key) {
//         console.log(string,"aaaaa");
//         console.log(key,"bbb")
//         if (string === "") {
//             return "";
//         }
//         string = buffer.Buffer.from(string,"utf8").toString('base64');
//         let v = this.stringToLongArray(string, true)
//             , k = this.stringToLongArray(key, false)
//             , n = v.length - 1
//             , z = v[n]
//             , y = v[0]
//             , mx, e, p
//             , q = Math.floor(6 + 52 / (n + 1))
//             , sum = 0;
//         if (k.length < 4) {
//             k.length = 4;
//         }
    
//         while (0 <= --q) {
//             sum = sum + delta & 0xffffffff;
//             e = sum >>> 2 & 3;
//             for (p = 0; p < n; ++p) {
//                 y = v[p + 1];
//                 mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
//                 z = v[p] = v[p] + mx & 0xffffffff;
//             }
//             y = v[0];
//             mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
//             z = v[n] = v[n] + mx & 0xffffffff;
//         }
//         return this.longArrayToString(v, false);
//     }
    
    
//     public static decrypt(string, key) {
//         if (string === "") {
//             return "";
//         }
//         let v = this.stringToLongArray(string, false);
//         let k = this.stringToLongArray(key, false);
//         if (k.length < 4) {
//             k.length = 4;
//         }
//         let n = v.length - 1;
    
//         let z = v[n - 1], y = v[0];
//         let mx, e, p, q = Math.floor(6 + 52 / (n + 1)), sum = q * delta & 0xffffffff;
//         while (sum !== 0) {
//             e = sum >>> 2 & 3;
//             for (p = n; p > 0; --p) {
//                 z = v[p - 1];
//                 mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
//                 y = v[p] = v[p] - mx & 0xffffffff;
//             }
//             z = v[n];
//             mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
//             y = v[0] = v[0] - mx & 0xffffffff;
//             sum = sum - delta & 0xffffffff;
//         }
//         return buffer.Buffer.from(this.longArrayToString(v, true), 'base64').toString("utf8");
//     }
// }


/**********************************************************\
|                                                          |
| xxtea.js                                                 |
|                                                          |
| XXTEA encryption algorithm library for Node.js.          |
|                                                          |
| Encryption Algorithm Authors:                            |
|      David J. Wheeler                                    |
|      Roger M. Needham                                    |
|                                                          |
| Code Author: Ma Bingyao <mabingyao@gmail.com>            |
| LastModified: Jan 13, 2023                               |
|                                                          |
\**********************************************************/

/*jshint node:true, eqeqeq:true */
'use strict';

var delta = 0x9E3779B9;
export class xxtea{
    private static toUint8Array(v, includeLength) {
        var length = v.length;
        var n = length << 2;
        if (includeLength) {
            var m = v[length - 1];
            n -= 4;
            if ((m < n - 3) || (m > n)) {
                return null;
            }
            n = m;
        }
        var bytes = new Uint8Array(n);
        for (var i = 0; i < n; ++i) {
            bytes[i] = v[i >> 2] >> ((i & 3) << 3);
        }
        return bytes;
    }
    
    private static toUint32Array(bytes, includeLength) {
        var length = bytes.length;
        var n = length >> 2;
        if ((length & 3) !== 0) {
            ++n;
        }
        var v;
        if (includeLength) {
            v = new Uint32Array(n + 1);
            v[n] = length;
        }
        else {
            v = new Uint32Array(n);
        }
        for (var i = 0; i < length; ++i) {
            v[i >> 2] |= bytes[i] << ((i & 3) << 3);
        }
        return v;
    }
    
    private static mx(sum, y, z, p, e, k) {
        return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
    }
    
    private static fixk(k) {
        if (k.length < 16) {
            var key = new Uint8Array(16);
            key.set(k);
            k = key;
        }
        return k;
    }
    
    private static encryptUint32Array(v, k) {
        var length = v.length;
        var n = length - 1;
        var y, z, sum, e, p, q;
        z = v[n];
        sum = 0;
        for (q = Math.floor(6 + 52 / length) | 0; q > 0; --q) {
            sum += delta;
            e = sum >>> 2 & 3;
            for (p = 0; p < n; ++p) {
                y = v[p + 1];
                z = v[p] += this.mx(sum, y, z, p, e, k);
            }
            y = v[0];
            z = v[n] += this.mx(sum, y, z, p, e, k);
        }
        return v;
    }
    
    private static decryptUint32Array(v, k) {
        var length = v.length;
        var n = length - 1;
        var y, z, sum, e, p, q;
        y = v[0];
        q = Math.floor(6 + 52 / length);
        for (sum = q * delta; sum !== 0; sum -= delta) {
            e = sum >>> 2 & 3;
            for (p = n; p > 0; --p) {
                z = v[p - 1];
                y = v[p] -= this.mx(sum, y, z, p, e, k);
            }
            z = v[n];
            y = v[0] -= this.mx(sum, y, z, p, e, k);
        }
        return v;
    }
    
    private static toBytes(str) {
        var n = str.length;
        // A single code unit uses at most 3 bytes.
        // Two code units at most 4.
        var bytes = new Uint8Array(n * 3);
        var length = 0;
        for (var i = 0; i < n; i++) {
            var codeUnit = str.charCodeAt(i);
            if (codeUnit < 0x80) {
                bytes[length++] = codeUnit;
            }
            else if (codeUnit < 0x800) {
                bytes[length++] = 0xC0 | (codeUnit >> 6);
                bytes[length++] = 0x80 | (codeUnit & 0x3F);
            }
            else if (codeUnit < 0xD800 || codeUnit > 0xDFFF) {
                bytes[length++] = 0xE0 | (codeUnit >> 12);
                bytes[length++] = 0x80 | ((codeUnit >> 6) & 0x3F);
                bytes[length++] = 0x80 | (codeUnit & 0x3F);
            }
            else {
                if (i + 1 < n) {
                    var nextCodeUnit = str.charCodeAt(i + 1);
                    if (codeUnit < 0xDC00 && 0xDC00 <= nextCodeUnit && nextCodeUnit <= 0xDFFF) {
                        var rune = (((codeUnit & 0x03FF) << 10) | (nextCodeUnit & 0x03FF)) + 0x010000;
                        bytes[length++] = 0xF0 | (rune >> 18);
                        bytes[length++] = 0x80 | ((rune >> 12) & 0x3F);
                        bytes[length++] = 0x80 | ((rune >> 6) & 0x3F);
                        bytes[length++] = 0x80 | (rune & 0x3F);
                        i++;
                        continue;
                    }
                }
                throw new Error('Malformed string');
            }
        }
        return bytes.subarray(0, length);
    }
    
    private static toShortString(bytes, n) {
        var charCodes = new Uint16Array(n);
        var i = 0, off = 0;
        for (var len = bytes.length; i < n && off < len; i++) {
            var unit = bytes[off++];
            switch (unit >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    charCodes[i] = unit;
                    break;
                case 12:
                case 13:
                    if (off < len) {
                        charCodes[i] = ((unit & 0x1F) << 6) |
                            (bytes[off++] & 0x3F);
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                case 14:
                    if (off + 1 < len) {
                        charCodes[i] = ((unit & 0x0F) << 12) |
                            ((bytes[off++] & 0x3F) << 6) |
                            (bytes[off++] & 0x3F);
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                case 15:
                    if (off + 2 < len) {
                        var rune = (((unit & 0x07) << 18) |
                            ((bytes[off++] & 0x3F) << 12) |
                            ((bytes[off++] & 0x3F) << 6) |
                            (bytes[off++] & 0x3F)) - 0x10000;
                        if (0 <= rune && rune <= 0xFFFFF) {
                            charCodes[i++] = (((rune >> 10) & 0x03FF) | 0xD800);
                            charCodes[i] = ((rune & 0x03FF) | 0xDC00);
                        }
                        else {
                            throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16));
                        }
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                default:
                    throw new Error('Bad UTF-8 encoding 0x' + unit.toString(16));
            }
        }
        if (i < n) {
            charCodes = charCodes.subarray(0, i);
        }
        return String.fromCharCode.apply(String, charCodes);
    }
    
    private static toLongString(bytes, n) {
        var buf = [];
        var charCodes = new Uint16Array(0x8000);
        var i = 0, off = 0;
        for (var len = bytes.length; i < n && off < len; i++) {
            var unit = bytes[off++];
            switch (unit >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    charCodes[i] = unit;
                    break;
                case 12:
                case 13:
                    if (off < len) {
                        charCodes[i] = ((unit & 0x1F) << 6) |
                            (bytes[off++] & 0x3F);
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                case 14:
                    if (off + 1 < len) {
                        charCodes[i] = ((unit & 0x0F) << 12) |
                            ((bytes[off++] & 0x3F) << 6) |
                            (bytes[off++] & 0x3F);
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                case 15:
                    if (off + 2 < len) {
                        var rune = (((unit & 0x07) << 18) |
                            ((bytes[off++] & 0x3F) << 12) |
                            ((bytes[off++] & 0x3F) << 6) |
                            (bytes[off++] & 0x3F)) - 0x10000;
                        if (0 <= rune && rune <= 0xFFFFF) {
                            charCodes[i++] = (((rune >> 10) & 0x03FF) | 0xD800);
                            charCodes[i] = ((rune & 0x03FF) | 0xDC00);
                        }
                        else {
                            throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16));
                        }
                    }
                    else {
                        throw new Error('Unfinished UTF-8 octet sequence');
                    }
                    break;
                default:
                    throw new Error('Bad UTF-8 encoding 0x' + unit.toString(16));
            }
            if (i >= 0x7FFF - 1) {
                var size = i + 1;
                buf.push(String.fromCharCode.apply(String, charCodes.subarray(0, size)));
                n -= size;
                i = -1;
            }
        }
        if (i > 0) {
            buf.push(String.fromCharCode.apply(String, charCodes.subarray(0, i)));
        }
        return buf.join('');
    }
    
    public static toString(bytes) {
        var n = bytes.length;
        if (n === 0) return '';
        return ((n < 0x7FFF) ?
            this.toShortString(bytes, n) :
            this.toLongString(bytes, n));
    }
    
    public static encrypt(data, key) {
        if (typeof data === 'string') data = this.toBytes(data);
        if (typeof key === 'string') key = this.toBytes(key);
        if (data === undefined || data === null || data.length === 0) {
            return data;
        }
        return this.toUint8Array(this.encryptUint32Array(this.toUint32Array(data, true), this.toUint32Array(this.fixk(key), false)), false);
    }
    
    private static encryptToString(data, key) {
        if (typeof Buffer.from === "function") {
            return Buffer.from(this.encrypt(data, key)).toString('base64');
        } else {
            return new Buffer(this.encrypt(data, key)).toString('base64');
        }
    }
    
    public static decrypt(data, key) {
        if (typeof data === 'string') {
            if (typeof Buffer.from === "function") {
                data = Buffer.from(data, 'base64');
            } else {
                data = new Buffer(data, 'base64');
            }
        }
        if (typeof key === 'string') key = this.toBytes(key);
        if (data === undefined || data === null || data.length === 0) {
            return data;
        }
        return this.toUint8Array(this.decryptUint32Array(this.toUint32Array(data, false), this.toUint32Array(this.fixk(key), false)), true);
    }
    
    private static decryptToString(data, key) {
        return this.toString(this.decrypt(data, key));
    }
    
}
